package app

import (
	"context"
	"errors"
	"fmt"
	"gitee.com/zhucheer/orange/cfg"
	"gitee.com/zhucheer/orange/internal"
	"gitee.com/zhucheer/orange/logger"
	"os"
	"sync"
	"time"
)

// JobSrv 控制台任务应用注册接口
type JobSrv interface {
	JobDispatch()
	Register()
}

var jobHandler *Job

type Job struct {
	handlerMap map[string]JobHandler
	mutex      sync.Mutex
}

type JobFunc func(ctx *context.Context) error

type JobHandler struct {
	Action  JobFunc
	Timeout time.Duration
}

func init() {
	jobHandler = &Job{
		handlerMap: make(map[string]JobHandler),
	}

	JobRun("orange", defaultJob, 1)
}

// AppendJob
func (j *Job) AppendJob(jobName string, handler JobHandler) {
	j.mutex.Lock()
	defer j.mutex.Unlock()

	j.handlerMap[jobName] = handler
}

// GetJob
func (j *Job) GetJob(jobName string) (handler JobHandler, err error) {
	j.mutex.Lock()
	defer j.mutex.Unlock()

	itemHandler, ok := j.handlerMap[jobName]
	if !ok {
		return handler, errors.New("job not found")
	}
	return itemHandler, nil
}

// JobRun 注册一个控制台任务
func JobRun(jobName string, runDo JobFunc, timeout time.Duration) {
	jobHandler.AppendJob(jobName, JobHandler{
		runDo,
		timeout,
	})
}

// runJobDispatch 根据注册任务名称执行任务
func runJobDispatch(jobName string) {
	if jobName == "" {
		logger.Warning("jobName is empty not run!")
		return
	}

	if jobHandler.handlerMap == nil || len(jobHandler.handlerMap) == 0 {
		logger.Warning("not found job registered")
		return
	}
	itemHandler, err := jobHandler.GetJob(jobName)
	if err != nil {
		logger.Warning("%s task is not registered, register it before use!", jobName)
		return
	}

	startTime := time.Now()
	var ctx context.Context
	if itemHandler.Timeout > 0 {
		// 有超时时间的任务
		ctx, _ = context.WithTimeout(context.TODO(), itemHandler.Timeout)

		go func() {
			select {

			case <-ctx.Done():
				logger.Errorw("jobRun error exec timeout", "cronName", jobName, "timeout", itemHandler.Timeout)
				internal.ConsoleLog(fmt.Sprintf("job run timeout exec cronName:%s, timeout:%v", jobName, itemHandler.Timeout))
				jobExit(ctx, 1)
			}
		}()

	} else {
		ctx = context.TODO()
	}
	internal.ConsoleLog("================console run============")
	err = itemHandler.Action(&ctx)
	costTime := time.Since(startTime)
	if err != nil {
		logger.Errorw("JobRun exec error", "jobName", jobName, "costMilliSeconds", costTime.Milliseconds(), "err", err)
	} else {
		logger.Infow("JobRun exec success", "jobName", jobName, "costMilliSeconds", costTime.Milliseconds())
	}
	jobExit(ctx, 0)
}

// jobExit 退出任务
func jobExit(ctx context.Context, code int) {
	// 发送应用退出信号
	sendAppStop()
	//处理后置操作等待结束
	appDeferDo(ctx)

	internal.ConsoleLog(fmt.Sprintf("job run exit code:%d", code))
	time.Sleep(time.Second)
	os.Exit(code)
}

// startJobSrv 启动http服务
func startJobSrv(jobSrv JobSrv) {
	jobSrv.JobDispatch()

	jobName := cfg.GetStringFlag("jobRun")
	runJobDispatch(jobName)

	time.Sleep(2 * time.Second)
	os.Exit(0)
}

// defaultJob
func defaultJob(ctx *context.Context) error {
	internal.ConsoleLog("enjoy your console job")
	return nil
}
